﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PageLayout.cs" company="Anton Frattaroli">
//   Copyright (c) Anton Frattaroli. All rights reserved.
// </copyright>
// <summary>
//   Defines the PageLayout class.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace Lidocaine
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.SqlClient;
    using System.Globalization;

    /// <summary>
    /// Defines the PageLayout class.
    /// </summary>
    public class PageLayout
    {
        #region Fields
        /// <summary>
        /// The identity of the database table's row where this page object is saved.
        /// </summary>
        private int pageID;

        /// <summary>
        /// Stores the page's virtual path.
        /// </summary>
        private string path;

        /// <summary>
        /// Stores the page's title.
        /// </summary>
        private string title;

        /// <summary>
        /// Stores the page's header ID.
        /// </summary>
        private int headerID;
        
        /// <summary>
        /// Stores the page's footer ID.
        /// </summary>
        private int footerID;
        
        /// <summary>
        /// Stores the page's css ID.
        /// </summary>
        private int cssID;
        
        /// <summary>
        /// Stores the page's js ID.
        /// </summary>
        private int javascriptID;
        
        /// <summary>
        /// Stores the page's content ID.
        /// </summary>
        private int contentID;

        /// <summary>
        /// Stores the page's HTemplate header.
        /// </summary>
        private Template header;

        /// <summary>
        /// Stores the page's HTemplate footer.
        /// </summary>
        private Template footer;

        /// <summary>
        /// Stores the page's HScript css.
        /// </summary>
        private Script css;

        /// <summary>
        /// Stores the page's HScript js.
        /// </summary>
        private Script js;

        /// <summary>
        /// Stores the page's HContent content.
        /// </summary>
        private Content content;

        /// <summary>
        /// Stores the page's main section HModule list.
        /// </summary>
        private IList<UserControlAsset> mainModules;

        /// <summary>
        /// Stores the page's right section HModule list.
        /// </summary>
        private IList<UserControlAsset> rightModules;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the PageLayout class.
        /// </summary>
        public PageLayout()
        {
        }

        /// <summary>
        /// Initializes a new instance of the PageLayout class.
        /// </summary>
        /// <param name="pagePath">The virtual path for this page.</param>
        public PageLayout(string pagePath)
        {
            using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("Lidocaine.GetPageByPath", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@PagePath", pagePath);
                    using (SqlDataReader rdr = cmd.ExecuteReader())
                    {
                        if (rdr.HasRows)
                        {
                            rdr.Read();
                            this.pageID = Convert.ToInt32(rdr["PageID"], CultureInfo.InvariantCulture);
                            this.path = pagePath;
                            this.title = rdr["PageTitle"] != DBNull.Value ? rdr["PageTitle"].ToString() : string.Empty;
                            this.headerID = rdr["PageHeader"] != DBNull.Value ? Convert.ToInt32(rdr["PageHeader"], CultureInfo.InvariantCulture) : 0;
                            this.footerID = rdr["PageFooter"] != DBNull.Value ? Convert.ToInt32(rdr["PageFooter"], CultureInfo.InvariantCulture) : 0;
                            this.cssID = rdr["PageCSS"] != DBNull.Value ? Convert.ToInt32(rdr["PageCSS"], CultureInfo.InvariantCulture) : 0;
                            this.javascriptID = rdr["PageJS"] != DBNull.Value ? Convert.ToInt32(rdr["PageJS"], CultureInfo.InvariantCulture) : 0;
                            this.contentID = rdr["PageContent"] != DBNull.Value ? Convert.ToInt32(rdr["PageContent"], CultureInfo.InvariantCulture) : 0;
                        }
                        else
                        {
                            throw new DataException("Page does not exist.");
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the PageLayout class.
        /// </summary>
        /// <param name="pageId">The identity of the database table's row referencing this page.</param>
        public PageLayout(int pageId)
        {
            using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("Lidocaine.GetPageByID", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@PageID", pageId);
                    using (SqlDataReader rdr = cmd.ExecuteReader())
                    {
                        if (rdr.HasRows)
                        {
                            rdr.Read();
                            this.pageID = pageId;
                            this.path = rdr["PagePath"].ToString();
                            this.title = rdr["PageTitle"] != DBNull.Value ? rdr["PageTitle"].ToString() : string.Empty;
                            this.headerID = rdr["PageHeader"] != DBNull.Value ? Convert.ToInt32(rdr["PageHeader"], CultureInfo.InvariantCulture) : 0;
                            this.footerID = rdr["PageFooter"] != DBNull.Value ? Convert.ToInt32(rdr["PageFooter"], CultureInfo.InvariantCulture) : 0;
                            this.cssID = rdr["PageCSS"] != DBNull.Value ? Convert.ToInt32(rdr["PageCSS"], CultureInfo.InvariantCulture) : 0;
                            this.javascriptID = rdr["PageJS"] != DBNull.Value ? Convert.ToInt32(rdr["PageJS"], CultureInfo.InvariantCulture) : 0;
                            this.contentID = rdr["PageContent"] != DBNull.Value ? Convert.ToInt32(rdr["PageContent"], CultureInfo.InvariantCulture) : 0;
                        }
                        else
                        {
                            throw new DataException("Page does not exist.");
                        }
                    }
                }
            }
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets the page's database row identity.
        /// </summary>
        public int PageId
        {
            get
            {
                return this.pageID;
            }
        }

        /// <summary>
        /// Gets or sets the page's virtual path.
        /// </summary>
        public string Path
        {
            get
            {
                return this.path;
            }

            set
            {
                this.path = value;
            }
        }

        /// <summary>
        /// Gets or sets the page's title.
        /// </summary>
        public string Title
        {
            get
            {
                return this.title;
            }

            set
            {
                this.title = value;
            }
        }

        /// <summary>
        /// Gets or sets the page's header.
        /// </summary>
        public Template Header
        {
            get
            {
                if (this.header == null && this.headerID != 0)
                {
                    this.header = new Template(this.headerID);
                }
                
                return this.header;
            }

            set
            {
                this.header = value;
                if (value != null)
                {
                    this.headerID = value.TemplateId;
                }
                else
                {
                    this.headerID = 0;
                }
            }
        }

        /// <summary>
        /// Gets or sets the page's footer.
        /// </summary>
        public Template Footer
        {
            get
            {
                if (this.footer == null && this.footerID != 0)
                {
                    this.footer = new Template(this.footerID);
                }
                
                return this.footer;
            }

            set
            {
                this.footer = value;
                if (value != null)
                {
                    this.footerID = value.TemplateId;
                }
                else
                {
                    this.footerID = 0;
                }
            }
        }
        
        /// <summary>
        /// Gets or sets the page's style sheet.
        /// </summary>
        public Script Css
        {
            get
            {
                if (this.css == null && this.cssID != 0)
                {
                    this.css = new Script(this.cssID);
                }
                
                return this.css;
            }

            set
            {
                this.css = value;
                if (value != null)
                {
                    this.cssID = value.ScriptId;
                }
                else
                {
                    this.cssID = 0;
                }
            }
        }
        
        /// <summary>
        /// Gets or sets the page's javascript.
        /// </summary>
        public Script JS
        {
            get
            {
                if (this.js == null && this.javascriptID != 0)
                {
                    this.js = new Script(this.javascriptID);
                }
                
                return this.js;
            }

            set
            {
                this.js = value;
                if (value != null)
                {
                    this.javascriptID = value.ScriptId;
                }
                else
                {
                    this.javascriptID = 0;
                }
            }
        }

        /// <summary>
        /// Gets or sets the page's content.
        /// </summary>
        public Content Content
        {
            get
            {
                if (this.content == null && this.contentID != 0)
                {
                    this.content = new Content(this.contentID);
                }
                
                return this.content;
            }

            set
            {
                this.content = value;
                this.contentID = value.ContentId;
            }
        }

        /// <summary>
        /// Gets the page's main modules.
        /// </summary>
        public IList<UserControlAsset> MainModules 
        {
            get
            {
                if (this.mainModules == null)
                {
                    List<UserControlAsset> m = new List<UserControlAsset>();
                    using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
                    {
                        conn.Open();
                        using (SqlCommand cmd = new SqlCommand("Lidocaine.GetPageMainModules", conn))
                        {
                            cmd.CommandType = CommandType.StoredProcedure;
                            cmd.Parameters.AddWithValue("@ID", this.pageID);
                            using (SqlDataReader rdr = cmd.ExecuteReader())
                            {
                                if (rdr.HasRows)
                                {
                                    while (rdr.Read())
                                    {
                                        m.Add(new UserControlAsset(Convert.ToInt32(rdr["ModuleID"], CultureInfo.InvariantCulture)));
                                    }
                                }
                            }
                        }
                    }

                    this.mainModules = m.AsReadOnly();
                }

                return this.mainModules;
            }
        }

        /// <summary>
        /// Gets the page's right modules.
        /// </summary>
        public IList<UserControlAsset> RightModules
        {
            get
            {
                if (this.rightModules == null)
                {
                    List<UserControlAsset> m = new List<UserControlAsset>();
                    using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
                    {
                        conn.Open();
                        using (SqlCommand cmd = new SqlCommand("Lidocaine.GetPageRightModules", conn))
                        {
                            cmd.CommandType = CommandType.StoredProcedure;
                            cmd.Parameters.AddWithValue("@ID", this.pageID);
                            using (SqlDataReader rdr = cmd.ExecuteReader())
                            {
                                if (rdr.HasRows)
                                {
                                    while (rdr.Read())
                                    {
                                        m.Add(new UserControlAsset(Convert.ToInt32(rdr["ModuleID"], CultureInfo.InvariantCulture)));
                                    }
                                }
                            }
                        }
                    }

                    this.rightModules = m.AsReadOnly();
                }

                return this.rightModules;
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Removes a navigation group from the page.
        /// </summary>
        /// <param name="ordinal">The ordinal of the navigation group to remove.</param>
        /// <param name="area">The template location of the navigation group to remove.</param>
        public void RemoveNavigationGroupAt(int ordinal, NavigationGroupArea area)
        {
            using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("Lidocaine.DeletePageNavGroup", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@NavGroupArea", area);
                    cmd.Parameters.AddWithValue("@NavGroupOrder", ordinal);
                    cmd.Parameters.AddWithValue("@PageID", this.pageID);
                    cmd.ExecuteNonQuery();
                }
            }
        }
        
        /// <summary>
        /// Appends a navigation group to the page.
        /// </summary>
        /// <param name="navigationGroup">The navigation group to append.</param>
        public void AppendNavigationGroup(NavigationGroup navigationGroup)
        {
            using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("Lidocaine.InsertPageNavGroup", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@PageID", this.pageID);
                    cmd.Parameters.AddWithValue("@NavGroupName", navigationGroup.Name);
                    cmd.Parameters.AddWithValue("@NavGroupOrder", this.GetNavigationGroups(navigationGroup.Area).Count + 1);
                    cmd.Parameters.AddWithValue("@NavGroupArea", navigationGroup.Area);
                    cmd.ExecuteNonQuery();
                }
            }
        }
                
        /// <summary>
        /// Reorders page navigation groups.
        /// </summary>
        /// <param name="oldOrdinal">The existing ordinal of the navigation group to reorder.</param>
        /// <param name="newOrdinal">The new ordinal of the navigation group to reorder.</param>
        /// <param name="area">The template location of the navigation group to reorder.</param>
        public void ReorderNavigationGroups(int oldOrdinal, int newOrdinal, NavigationGroupArea area)
        {
            using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("Lidocaine.UpdatePageNavGroupOrder", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@OrderNew", newOrdinal);
                    cmd.Parameters.AddWithValue("@OrderOld", oldOrdinal);
                    cmd.Parameters.AddWithValue("@PageID", this.pageID);
                    cmd.Parameters.AddWithValue("@NavGroupArea", area);
                    cmd.ExecuteNonQuery();
                }
            }
        }

        /// <summary>
        /// Loads all content versions.
        /// </summary>
        /// <returns>A read-only list of page content versions.</returns>
        public IList<Content> GetPageContentVersionsInfo()
        {
            List<Content> contents = new List<Content>();
            using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("Lidocaine.GetPageContentBlocks", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@ID", this.PageId);
                    using (SqlDataReader rdr = cmd.ExecuteReader())
                    {
                        if (rdr.HasRows)
                        {
                            while (rdr.Read())
                            {
                                contents.Add(new Content(Convert.ToInt32(rdr["ID"], CultureInfo.InvariantCulture)));
                            }
                        }
                    }
                }
            }

            return contents.AsReadOnly();
        }

        /// <summary>
        /// Loads the navigation groups located in the desired area.
        /// </summary>
        /// <param name="area">The area the navigation groups are intended to go.</param>
        /// <returns>A read-only list of navigation groups.</returns>
        public IList<NavigationGroup> GetNavigationGroups(NavigationGroupArea area)
        {
            List<NavigationGroup> navGroups = new List<NavigationGroup>();
            using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("Lidocaine.GetPageNavGroups", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@PageID", this.pageID);
                    cmd.Parameters.AddWithValue("@NavGroupArea", area);
                    using (SqlDataReader rdr = cmd.ExecuteReader())
                    {
                        if (rdr.HasRows)
                        {
                            while (rdr.Read())
                            {
                                navGroups.Add(new NavigationGroup(rdr["NavGroupName"].ToString(), area));
                            }
                        }
                    }
                }
            }

            return navGroups.AsReadOnly();
        }

        /// <summary>
        /// Removes a module from the main column at the specified ordinal.
        /// </summary>
        /// <param name="ordinal">The ordinal of the targeted module.</param>
        public void DeleteMainModule(int ordinal)
        {
            using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("Lidocaine.DeletePageMainModule", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@ContentOrder", ordinal);
                    cmd.Parameters.AddWithValue("@PageID", this.pageID);
                    cmd.ExecuteNonQuery();
                }
            }
        }

        /// <summary>
        /// Removes a module from the right column at the specified ordinal.
        /// </summary>
        /// <param name="ordinal">The ordinal of the targeted module.</param>
        public void DeleteRightModule(int ordinal)
        {
            using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("Lidocaine.DeletePageRightModule", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@ContentOrder", ordinal);
                    cmd.Parameters.AddWithValue("@PageID", this.pageID);
                    cmd.ExecuteNonQuery();
                }
            }
        }

        /// <summary>
        /// Saves page updates to database.
        /// </summary>
        public void Save()
        {
            using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("Lidocaine.UpdatePage", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@PageID", this.pageID);
                    cmd.Parameters.AddWithValue("@PagePath", this.path);
                    cmd.Parameters.AddWithValue("@PageTitle", this.title);
                    if (this.headerID != 0)
                    {
                        cmd.Parameters.AddWithValue("@PageHeader", this.headerID);
                    }
                    else
                    {
                        cmd.Parameters.AddWithValue("@PageHeader", DBNull.Value);
                    }

                    if (this.footerID != 0)
                    {
                        cmd.Parameters.AddWithValue("@PageFooter", this.footerID);
                    }
                    else
                    {
                        cmd.Parameters.AddWithValue("@PageFooter", DBNull.Value);
                    }

                    if (this.cssID != 0)
                    {
                        cmd.Parameters.AddWithValue("@PageCSS", this.cssID);
                    }
                    else
                    {
                        cmd.Parameters.AddWithValue("@PageCSS", DBNull.Value);
                    }

                    if (this.javascriptID != 0)
                    {
                        cmd.Parameters.AddWithValue("@PageJS", this.javascriptID);
                    }
                    else
                    {
                        cmd.Parameters.AddWithValue("@PageJS", DBNull.Value);
                    }

                    cmd.Parameters.AddWithValue("@PageContent", this.contentID);
                    cmd.ExecuteNonQuery();
                }
            }
        }

        /// <summary>
        /// Reorders page modules.
        /// </summary>
        /// <param name="oldOrdinal">The existing ordinal of the module to reorder.</param>
        /// <param name="newOrdinal">The new ordinal of the module to reorder.</param>
        /// <param name="area">The template location of the module to reorder.</param>
        public void ReorderModules(int oldOrdinal, int newOrdinal, UserControlArea area)
        {
            using (SqlConnection conn = new SqlConnection(Environmental.ConnectionString))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("Lidocaine.UpdatePageModuleOrder", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@OrderNew", newOrdinal);
                    cmd.Parameters.AddWithValue("@OrderOld", oldOrdinal);
                    cmd.Parameters.AddWithValue("@PageID", this.pageID);
                    cmd.Parameters.AddWithValue("@ModuleArea", area);
                    cmd.ExecuteNonQuery();
                }
            }
        }
        #endregion
    }
}